%{
open Ast
open Core_kernel.Std
open Semantics
%}

%token EOF
%token <int> Int
%token <int> BitVector
%token <string> Ident
%token Dash
%token Arrow
%token LParen
%token RParen
%token LBrack
%token RBrack
%token LBrace
%token RBrace
%token Dot
%token Semi
%token Colon
%token Eq
(* %token AtKeyword *)
%token K_declare
%token K_encoding
%token K_feedback
%token K_input
%token K_module
%token K_output
%token K_parameter
%token K_path
%token K_pipeline
%token K_stage
%token K_wire

%start <Ast.toplevel> top
%start <bool * (string * Vlog.port) list> bb_spec
%type <ast_blackbox> module_entry
%type <ast_blackbox list> module_block
%type <ast_wire list> wire_block
%type <ast_datapath list> datapath_block
%type <string list> pipeline_decl
%type <string * ast_stage> stage_decl

%%

top:
  | EOF { empty_toplevel }
  | l = module_block;   t = top { t.top_modules <- l @ t.top_modules; t }
  | l = input_block;    t = top { t.top_ports   <- l @ t.top_ports  ; t }
  | l = output_block;   t = top { t.top_ports   <- l @ t.top_ports  ; t }
  | l = wire_block;     t = top { t.top_wires   <- l @ t.top_wires  ; t }
  | l = datapath_block  t = top { t.top_paths   <- l @ t.top_paths  ; t }
  | p = pipeline_decl;  t = top { t.top_pipeline <- p; t }
  | s = stage_decl;     t = top { t.top_stages <- s :: t.top_stages; t }
  | e = encoding_block; t = top { t.top_encodings <- e; t }

parameter_assignment:
  | name = Ident; Eq; value = Int; Semi { name, value }

parameter_block:
  | K_parameter; LBrace; params = list(parameter_assignment); RBrace { params }

module_entry:
  | type_name = Ident; params = parameter_block?; inst_name = Ident?; Semi
    { Option.value inst_name ~default:type_name, (type_name, Option.value params ~default:[]) }

module_block:
  | K_module; LBrace; m = list(module_entry); RBrace { m }

path:
  | p = separated_nonempty_list(Dot, Ident) {p}

part_selector:
  | LBrack; hi = Int; lo_opt = option(preceded(Colon, Int)); RBrack
    { Option.value lo_opt ~default:hi, hi+1 }

endpoint:
  | ep_path = path; ep_range = option(part_selector) {{ ep_path; ep_range }}

datum:
  | size = Int; value = BitVector { size, value }

driver:
  | e = endpoint { Ast_driver_var e }
  | d = datum { let width, value = d in Ast_driver_const (width, value) }

wire:
  | pfrom = driver; Dash; pto = endpoint; Semi { pfrom, pto }

labeled_wire:
  | pfrom = driver; Dash; pto = endpoint;
    labels_opt = option(delimited(LParen, list(Ident), RParen)); Semi
    { pfrom, pto, labels_opt }

wire_block:
  | K_wire; LBrace; w = list(wire); RBrace {w}

labeled_wire_block:
  | K_wire; LBrace; w = list(labeled_wire); RBrace {w}

datapath:
  | pfrom = driver; Arrow; pto = endpoint;
    labels_opt = option(delimited(LParen, list(Ident), RParen)); Semi
    { pfrom, pto, labels_opt }

datapath_block:
  | K_path; LBrace; p = list(datapath); RBrace {p}

pipeline_decl:
  | K_pipeline; p = separated_nonempty_list(Arrow, Ident); Semi {p}

stage_content:
  | (* empty *) {{ stage_ports = []; stage_modules = []; stage_wires = []; stage_feedback_wires_opt = None }}
  | m = module_block; s = stage_content
    {{ s with stage_modules = m @ s.stage_modules }}
  | i = input_block; s = stage_content
    {{ s with stage_ports = i @ s.stage_ports }}
  | o = output_block; s = stage_content
    {{ s with stage_ports = o @ s.stage_ports }}
  | w = labeled_wire_block; s = stage_content
    {{ s with stage_wires = w @ s.stage_wires }}
  | K_feedback; w = datapath_block; s = stage_content
    {{ s with stage_feedback_wires_opt = Some w }}

stage_decl:
  | K_stage; name = Ident; LBrace; s = stage_content; RBrace { name, s }

bb_port:
  | name = Ident; width_opt = option(delimited(LBrack, Int, RBrack)); Semi
    { name, Option.value width_opt ~default:1 }

input_block:
  | K_input; ports = delimited(LBrace, list(bb_port), RBrace)
    { List.map ports ~f:(fun (name, width) -> (name, (width, Vlog.Input))) }

output_block:
  | K_output; ports = delimited(LBrace, list(bb_port), RBrace)
    { List.map ports ~f:(fun (name, width) -> (name, (width, Vlog.Output))) }

bb_spec:
  | decl_keywords_opt = declare_stmt?; bb_input = input_block; bb_output = output_block
    { let decl_keywords = Option.value decl_keywords_opt ~default:[] in
      List.mem decl_keywords "has_clock",
      bb_input @ bb_output }

declare_stmt:
  | K_declare; keywords = list(Ident); Semi { keywords }

encoding_block:
  | K_encoding; LBrace; e = list(terminated(Ident, Semi)); RBrace { e }
